package com.jctrip.hotel.source.service.hmein.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.google.common.collect.Lists;
import com.jctrip.hotel.common.util.*;
import com.jctrip.hotel.domain.dao.HtBrandStdInfoDao;
import com.jctrip.hotel.domain.entity.*;
import com.jctrip.hotel.source.mq.IncrHotelRocketMqProducer;
import com.jctrip.hotel.source.model.hmein.HmeinHotelRoomStatus;
import com.jctrip.hotel.source.service.AsyncService;
import com.jctrip.hotel.source.service.basic.*;
import com.jctrip.hotel.source.xxl.hmein.thread.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jctrip.hotel.common.constant.RedisKeyConstant;
import com.jctrip.hotel.common.constant.XxlJobNameConstant;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.threadpool.XxlJobThreadPool;
import com.jctrip.hotel.domain.dao.HtHotelStdInfoDao;
import com.jctrip.hotel.source.client.HworldCallBackClient;
import com.jctrip.hotel.source.model.hmein.resp.CityListResult.CityList;
import com.jctrip.hotel.source.model.hmein.resp.HotelFacilitiesResult.DClassList;
import com.jctrip.hotel.source.model.hmein.resp.HotelFacilitiesResult.FacilitiesList;
import com.jctrip.hotel.source.model.hmein.resp.HotelFacilitiesResult.HotelFacilitiesList;
import com.jctrip.hotel.source.model.hmein.resp.HotelInfosResult.HotelInfos;
import com.jctrip.hotel.source.model.hmein.resp.HotelRmMcResult.HotelRmMc;
import com.jctrip.hotel.source.model.hmein.resp.HotelRmMcResult.RoomDespDic;
import com.jctrip.hotel.source.model.hmein.resp.RoomStatusResult;
import com.jctrip.hotel.source.model.hmein.resp.RoomStatusResult.RoomRateCancelRule;
import com.jctrip.hotel.source.model.hmein.resp.RoomStatusResult.RoomRateDetailDailys;
import com.jctrip.hotel.source.model.hmein.resp.RoomStatusResult.RoomRateGuaranteeRules;
import com.jctrip.hotel.source.model.hmein.resp.RoomStatusResult.RoomRates;
import com.jctrip.hotel.source.model.hmein.resp.RoomStatusResult.RoomType;
import com.jctrip.hotel.source.service.hmein.HmeinService;
import com.jctrip.hotel.source.service.hmein.HmeinTaskService;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author henry
 * @Date 2023/6/3 17:20
 * @Description
 */
@Slf4j
@Service
public class HmeinTaskServiceImpl implements HmeinTaskService {
    private static final long PUSH_EXPIRY = 1 * 60;
    private static final XxlJobThreadPool hotelIdListTaskFastPopPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_HMEIN_HOTEL_ID_LIST_TASK_POP);
    private static final XxlJobThreadPool ratesTaskFastPopPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_HMEIN_RATES_TASK_POP);
    private static final XxlJobThreadPool ratesLongerTaskFastPopPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_HMEIN_LONGER_RATES_TASK_POP);
    private static final XxlJobThreadPool ratesHotspotTaskFastPopPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_HMEIN_HOTSPOT_RATES_TASK_POP);
    private static final XxlJobThreadPool roomRepairTaskPopPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_HMEIN_ROOM_REPAIR_TASK_POP);
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private HmeinService hmeinService;
    @Resource
    private HtHotelStdInfoService htHotelStdInfoService;
    @Resource
    private HtRoomTypeStdInfoService htRoomTypeStdInfoService;
    @Resource
    private HtHotelStdInfoDao htHotelStdInfoDao;
    @Autowired
    private HtPriceCalendarHmeinService htPriceCalendarHmeinService;
    @Resource
    private HtPriceCalendarService htPriceCalendarService;
    @Resource
    private HworldCallBackClient hworldCallBackClient;
    @Autowired
    private HtHotelStdImageService htHotelStdImageService;
    @Autowired
    private HtBrandStdInfoDao htBrandStdInfoDao;
    @Autowired
    private IncrHotelRocketMqProducer incrHotelRocketMqProducer;

    @Override
    public void syncHmeinHotelIdListTask(String appId, String hotelId) {
        HotelInfos hotelInfo = hmeinService.getHotelInfo(appId, hotelId);
        List<CityList> cityData = hmeinService.getCityData(appId);
        CityList city =
                cityData.stream().filter(cityList -> cityList.getCD().equals(hotelInfo.getS_CityCode())).findFirst()
                        .orElse(new CityList());
        HtHotelStdInfo htHotelStdInfo = parseHotelInfoData(hotelInfo, city.getDescript());


        List<HotelFacilitiesList> hotelFacilitiesListData = hmeinService.getHotelFacilitiesListData(appId, hotelId);
        List<Integer> enums = Arrays.asList(12, 13, 102, 103, 477);
        List<DClassList> classListList = new ArrayList<>();
        Optional.ofNullable(hotelFacilitiesListData).orElse(new ArrayList<>()).forEach(hotelFacilitiesList -> {
            Optional.ofNullable(hotelFacilitiesList).map(HotelFacilitiesList::getFacilitiesList)
                    .orElse(new ArrayList<>()).forEach(facilitiesList -> {
                        classListList.addAll(
                                Optional.ofNullable(facilitiesList).map(FacilitiesList::getD_ClassList)
                                        .orElse(new ArrayList<>()).stream()
                                        .filter(dClassList -> enums.contains(dClassList.getD_No()))
                                        .collect(Collectors.toList()));
                    });
        });
        htHotelStdInfo.setSupportRestaurant(CollectionUtils.isNotEmpty(classListList) ? 1 : 0);
        // 基础信息
        htHotelStdInfoService.saveOrUpdate(htHotelStdInfo,
                new LambdaQueryWrapper<HtHotelStdInfo>().eq(HtHotelStdInfo::getHotelId,
                        htHotelStdInfo.getHotelId()));

        // 房型
        List<HotelRmMc> roomTypeList = hmeinService.getRoomType(appId, hotelId);
        for (HotelRmMc hotelRmMc : roomTypeList) {
            // 房型
            HtRoomTypeStdInfo roomType = new HtRoomTypeStdInfo();
            roomType.setHotelId(htHotelStdInfo.getHotelId());
            roomType.setRoomTypeCode(hotelRmMc.getStRmTypeCd());
            roomType.setRoomTypeName(hotelRmMc.getRmTypeDesp());
            RoomDespDic roomDespDic = hotelRmMc.getRoomDespDic();
            roomType.setRoomArea(AreaUtil.parseRoomArea(roomDespDic.getRoomArea()));
            if (StringUtils.isNotBlank(roomDespDic.getCount())) {
                // 笑死 2-3人 怎么映射呢
                //                roomType.setMaxCheckIn(Integer.valueOf(roomDespDic.getCount()));
            }
            Integer window = 0;
            if (StringUtils.isNotBlank(roomDespDic.getWindow()) && roomDespDic.getWindow().equals("有窗")) {
                window = 1;
            }
            roomType.setWindowType(window);
            htRoomTypeStdInfoService.saveOrUpdate(roomType,
                    new LambdaQueryWrapper<HtRoomTypeStdInfo>().eq(HtRoomTypeStdInfo::getHotelId, roomType.getHotelId())
                            .eq(HtRoomTypeStdInfo::getRoomTypeCode, roomType.getRoomTypeCode()));
        }
    }


    private HtHotelStdInfo parseHotelInfoData(HotelInfos hotelData, String cityName) {
        if (hotelData == null) {
            return null;
        }

        HtHotelStdInfo hotelInfoData = new HtHotelStdInfo();
        hotelInfoData.setSourceCode(SourceEnum.HMEIN.getCode());
        hotelInfoData.setHotelId(hmeinService.parseInnId(hotelData.getS_HotelCd()));
        hotelInfoData.setHotelName(hotelData.getS_HotelNm());
        hotelInfoData.setHotelNameEn(hotelData.getS_HotelNm_En());
        hotelInfoData.setBrandCode(hotelData.getS_HotelClass());
        hotelInfoData.setBrandName(selectBrandName(SourceEnum.HMEIN.getCode(), hotelData.getS_HotelClass()));

        hotelInfoData.setHotelAddress(hotelData.getS_sAddress());
        hotelInfoData.setHotelTel(hotelData.getS_Tel());
        if (StringUtils.isNotBlank(cityName) && cityName.endsWith("市")) {
            cityName = cityName.substring(0, cityName.length() - 1);
        }
        hotelInfoData.setCityName(cityName);
        hotelInfoData.setIntroduction(hotelData.getS_Notice());
        hotelInfoData.setBookStatus(1);
        hotelInfoData.setSupportForeignGuest(null);
        hotelInfoData.setLatitude(BigDecimal.valueOf(hotelData.getLat()));
        hotelInfoData.setLongitude(BigDecimal.valueOf(hotelData.getLon()));
        hotelInfoData.setMapPointType(3);
        return hotelInfoData;
    }

    private String selectBrandName(String sourceCode, String brandCode) {
        HtBrandStdInfo htBrandStdInfo = htBrandStdInfoDao.selectOne(new LambdaQueryWrapper<HtBrandStdInfo>()
                .eq(HtBrandStdInfo::getSourceCode, sourceCode)
                .eq(HtBrandStdInfo::getBrandCode, brandCode));

        if (htBrandStdInfo == null) {
            return "";
        }
        return htBrandStdInfo.getBrandName();
    }

    @Override
    public void syncHmeinHotelIdListTaskFastPush(String appId) {
        if (redisUtil.setENx(RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTELID_PUSH, PUSH_EXPIRY,
                RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTELID_PUSH)) {
            List<String> hotelList = hmeinService.getHotelIds(appId);
            List<Object> ids = new ArrayList<>();
            ids.addAll(hotelList);
            redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTELID_LIST, ids);
        }
    }

    @Override
    public void syncHmeinHotelIdListTaskFastPop(String appId) {
        hotelIdListTaskFastPopPool.execute(() -> new SyncHmeinHotelIdTaskThread(appId));
    }

    @Override
    public void syncHmeinRatesTaskPush() {
        try {
            List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(
                    new LambdaQueryWrapper<HtHotelStdInfo>().eq(HtHotelStdInfo::getSourceCode,
                            SourceEnum.HMEIN.getCode()));
            if (CollectionUtils.isNotEmpty(htHotelStdInfos)
                    && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_MAIN_HMEIN_RATES_LIST) <= 0) {
                redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_HMEIN_RATES_LIST,
                        htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncHmeinLongerRatesTaskPush() {
        try {
            List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(
                    new LambdaQueryWrapper<HtHotelStdInfo>().eq(HtHotelStdInfo::getSourceCode,
                            SourceEnum.HMEIN.getCode()));
            if (CollectionUtils.isNotEmpty(htHotelStdInfos)
                    && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_MAIN_HMEIN_LONGER_RATES_LIST) <= 0) {
                redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_HMEIN_LONGER_RATES_LIST,
                        htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncHmeinRatesTaskFastPop(String appId, String memberNo, Integer days, Map<String, String> rateCodeWithSourceIdMap) {
        ratesTaskFastPopPool.execute(() -> new SyncHmeinRatesTaskThread(appId, memberNo, days, rateCodeWithSourceIdMap));
    }

    @Override
    public void syncHmeinLongerRatesTaskFastPop(String appId, String memberNo, Integer addDays, Integer days, Map<String, String> rateCodeWithSourceIdMap) {
        ratesLongerTaskFastPopPool.execute(
                () -> new SyncHmeinLongerRatesTaskThread(appId, memberNo, addDays, days, rateCodeWithSourceIdMap));
    }

    @Override
    public void syncHmeinHotspotRatesTaskPush() {
        if (redisUtil.setENx(RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTSPOT_RATES_PUSH, PUSH_EXPIRY,
                RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTSPOT_RATES_PUSH)) {
            try {
                List<String> hotels = hworldCallBackClient.getHotspotHotels().stream()
                        .filter(e -> e.startsWith(SourceEnum.HMEIN.getCode())).collect(Collectors.toList());
                if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(hotels)
                        && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTSPOT_RATES_LIST) <= 0) {
                    redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_HMEIN_HOTSPOT_RATES_LIST,
                            new ArrayList<>(hotels));
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    @Override
    public void syncHmeinHotspotRatesTaskFastPop(String appId, String memberNo, Integer days, Map<String, String> rateCodeWithSourceIdMap) {
        ratesHotspotTaskFastPopPool.execute(() -> new SyncHmeinHotspotRatesTaskThread(appId, memberNo, days, rateCodeWithSourceIdMap));
    }

    @Override
    public void syncHmeinRatesTaskPop(String appId, String hotelId, String memberNo, String start, String end, Map<String, String> rateCodeWithSourceIdMap) {
        // 查询房态
        RoomStatusResult roomStatus = hmeinService.getRoomStatus(appId, memberNo, hotelId, null, start, end);
        if (roomStatus.getFail() || CollectionUtils.isEmpty(roomStatus.getProducts())) {
            // 一律按照库存为0算 然后统一拉出数据处理 防止房态更新不及时，造成超卖
            htPriceCalendarHmeinService.update(new LambdaUpdateWrapper<HtPriceCalendarHmein>().set(HtPriceCalendarHmein::getQuota, 1)
                    .eq(HtPriceCalendarHmein::getHotelId, hmeinService.parseInnId(hotelId)));
            return;
        }

        // 找到当前酒店在中台数据库中的记录 先按房型码排序 再按日期排序
        List<HtPriceCalendarHmein> htPriceCalendars = htPriceCalendarHmeinService.list(new LambdaQueryWrapper<HtPriceCalendarHmein>()
                        .eq(HtPriceCalendarHmein::getHotelId, hmeinService.parseInnId(hotelId))).stream()
                .sorted(Comparator.comparing(HtPriceCalendarHmein::getRoomTypeCode)
                        .thenComparing(HtPriceCalendarHmein::getCalDate))
                .collect(Collectors.toList());
        AtomicBoolean incrFlag = new AtomicBoolean(false);

        // 存在当前数据库中的房型
        List<String> memoryRoomTypes = htPriceCalendars.stream()
                .map(HtPriceCalendarHmein::getRoomTypeCode)
                .distinct().collect(Collectors.toList());

        // 是否包含早餐 todo 不再通过餐厅来判读早餐数，但是当接口中无法读取对应活动价时，保底使用该早餐数判断
//        List<HotelFacilitiesList> hotelFacilitiesListData = hmeinService.getHotelFacilitiesListData(appId, hotelId);
//        List<Integer> enums = Arrays.asList(12, 13, 102, 103, 477);
//        List<DClassList> classListList = new ArrayList<>();
//        Optional.ofNullable(hotelFacilitiesListData).orElse(new ArrayList<>()).forEach(hotelFacilitiesList -> {
//            Optional.ofNullable(hotelFacilitiesList).map(HotelFacilitiesList::getFacilitiesList)
//                    .orElse(new ArrayList<>()).forEach(facilitiesList -> {
//                        classListList.addAll(
//                                Optional.ofNullable(facilitiesList).map(FacilitiesList::getD_ClassList)
//                                        .orElse(new ArrayList<>()).stream()
//                                        .filter(dClassList -> enums.contains(dClassList.getD_No()))
//                                        .collect(Collectors.toList()));
//                    });
//        });
//        int breakfastCount = CollectionUtils.isNotEmpty(classListList) ? 2 : 0;

        String rateCode = "BAR";
        HtHotelStdInfo htHotelStdInfo = htHotelStdInfoService.getOne(new LambdaQueryWrapper<HtHotelStdInfo>()
                .eq(HtHotelStdInfo::getHotelId, hmeinService.parseInnId(hotelId)));
        if (Objects.nonNull(htHotelStdInfo) && StringUtils.isNotBlank(htHotelStdInfo.getExtra())) {
            rateCode = (String) JsonUtil.toMap(htHotelStdInfo.getExtra()).get("RateCode");
        }
        String finalRateCode = rateCode;

        try {
            List<String> tempRoomTypes = roomStatus.getProducts().stream()
                    .map(RoomStatusResult.Products::getRoomType)
                    .map(RoomType::getRoomTypeCode)
                    .collect(Collectors.toList());

            memoryRoomTypes.removeAll(tempRoomTypes);
            if (CollectionUtils.isNotEmpty(memoryRoomTypes)) {
                incrFlag.set(true);
                memoryRoomTypes.forEach(roomTypeCode -> {
                    // 库存刷为0
                    updateQuota(hmeinService.parseInnId(hotelId), roomTypeCode);
                });
            }
        } catch (Exception e) {
            log.error("roomStatus-compare-error");
        }
        roomStatus.getProducts().forEach(products -> {
            RoomType roomType = products.getRoomType();
            // 门市价码-需要入库房态
            RoomRates roomRate =
                    products.getRoomRates().stream()
                            .filter(roomRates -> roomRates.getRateCode().equals(finalRateCode))
                            .findFirst().orElse(new RoomRates());

            // 活动价码 价格和早餐数
            List<String> rateCodeList = rateCodeWithSourceIdMap.keySet()
                    .stream()
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toList());
            List<RoomRates> activeRoomRatesList = products.getRoomRates().stream()
                    .filter(roomRates -> rateCodeList.contains(roomRates.getRateCode()))
                    .collect(Collectors.toList());

            // 找不到则不落
            if (CollectionUtils.isEmpty(activeRoomRatesList)) {
                return;
            }
            // 遍历门市价
            Optional.ofNullable(roomRate.getRoomRateDetailDailys()).orElse(new ArrayList<>()).forEach(roomRateDetailDailys -> {
                try {
                    // 找到对应日期,不同价码的早餐数和价格
                    List<HmeinHotelRoomStatus> hmeinHotelRoomStatus = getHmeinHotelRoomStatus(activeRoomRatesList, roomRateDetailDailys.getStDate());
//                    log.info(hotelId +" " +roomType.getRoomTypeCode() + " " +JsonUtil.toString(hmeinHotelRoomStatus));
                    // build 参数
                    HtPriceCalendarHmein htPriceCalendar = new HtPriceCalendarHmein();
                    htPriceCalendar.setHotelId(hmeinService.parseInnId(hotelId));
                    htPriceCalendar.setRoomTypeCode(roomType.getRoomTypeCode());
                    htPriceCalendar.setCurrencyCode("CNY");

                    HtRoomTypeStdInfo roomTypeStdInfo = htRoomTypeStdInfoService.getOne(
                            new LambdaQueryWrapper<HtRoomTypeStdInfo>().eq(HtRoomTypeStdInfo::getHotelId,
                                            htPriceCalendar.getHotelId())
                                    .eq(HtRoomTypeStdInfo::getRoomTypeCode, roomType.getRoomTypeCode()));
                    if (roomTypeStdInfo == null) {
                        return;
                    }
                    htPriceCalendar.setRoomTypeName(
                            roomTypeStdInfo == null ? null : roomTypeStdInfo.getRoomTypeName());
                    htPriceCalendar.setCalDate(
                            new SimpleDateFormat("yyyy-MM-dd").parse(roomRateDetailDailys.getStDate()));
                    htPriceCalendar.setQuota(roomRateDetailDailys.getAvailableRooms());
                    htPriceCalendar.setRetailPrice(roomRateDetailDailys.getPrs1());
                    // 没有变化的时候才走
                    if (!incrFlag.get()) {
                        // 获取当前房型 在当前日 中台的数据
                        HtPriceCalendarHmein roomCalDatePriceCalendar =
                                getRoomCalDatePriceCalendar(htPriceCalendar.getRoomTypeCode(), htPriceCalendar.getCalDate(), htPriceCalendars);
                        incrFlag.set(validatePrice(htPriceCalendar.getRetailPrice(), htPriceCalendar.getPurchasePrice(), htPriceCalendar.getQuota(), roomCalDatePriceCalendar));
                    }
                    List<RoomRateGuaranteeRules> roomRateGuaranteeRules = roomRate.getRoomRateGuaranteeRules();
                    if (CollectionUtils.isNotEmpty(roomRateGuaranteeRules)) {
                        RoomRateGuaranteeRules rule = roomRateGuaranteeRules.stream()
                                .filter(roomRateGuarantee -> finalRateCode.equals(roomRateGuarantee.getRateCode()))
                                .findFirst().orElse(null);
                        RoomRateCancelRule roomRateCancelRule = rule.getRoomRateCancelRule();
                        if (roomRateCancelRule != null) {
                            Integer ruleType = roomRateCancelRule.getRuleType();
                            if (ruleType != null) {
                                Integer cancelPolicyType = null;
                                switch (ruleType) {
                                    case 0:
                                    case 1:
                                    case 2:
                                        cancelPolicyType = 4;
                                        break;
                                    case 3:
                                        cancelPolicyType = 1;
                                        break;
                                }
                                htPriceCalendar.setCancelPolicyType(cancelPolicyType);
                            }
                        }
                        if (StringUtils.isNotBlank(roomRateCancelRule.getDayBeforeArrival())
                                && StringUtils.isNotBlank(
                                roomRateCancelRule.getCancelBeforeTime())) {
                            Integer days = Integer.valueOf(roomRateCancelRule.getDayBeforeArrival());
                            Date date = DateUtils.addDays(htPriceCalendar.getCalDate(), -days);
                            String cancelDays = new SimpleDateFormat("yyyy-MM-dd").format(date);
                            Integer time = Integer.valueOf(roomRateCancelRule.getCancelBeforeTime());
                            if (time != null && time < 10) {
                                cancelDays = cancelDays + " 0" + time + ":00:00";
                            } else {
                                cancelDays = cancelDays + " " + time + ":00:00";
                            }
                            htPriceCalendar.setLastCancelTime(
                                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(cancelDays));
                        }

                        htPriceCalendar.setCancelRule(JsonUtil.toString(roomRateCancelRule));
                    }

                    // 如果无法找到活动价，则添加门市价进行入库
                    if (CollectionUtils.isEmpty(hmeinHotelRoomStatus)) {
                        hmeinHotelRoomStatus.add(HmeinHotelRoomStatus.builder()
                                .price(roomRateDetailDailys.getPrs1())
                                .breakfastCount(0)
                                .build());
                    }
//                    log.info("hotelId : {}. roomType : {} , roomstatus : {}", hotelId, htPriceCalendar.getRoomTypeCode()
//                            , JsonUtil.toString(hmeinHotelRoomStatus));
                    hmeinHotelRoomStatus.forEach(e -> {
                        // 活动价
                        htPriceCalendar.setPurchasePrice(e.getPrice());
                        // 早餐数
                        htPriceCalendar.setBreakfastCount(e.getBreakfastCount());
                        // 活动码
                        htPriceCalendar.setProductId(e.getRateCode());
                        // 供应id
                        htPriceCalendar.setSourceId(rateCodeWithSourceIdMap.get(e.getRateCode()));

                        htPriceCalendar.setId(null);

                        // 保存房态信息
                        htPriceCalendarHmeinService.saveOrUpdate(htPriceCalendar,
                                new LambdaQueryWrapper<HtPriceCalendarHmein>().eq(HtPriceCalendarHmein::getHotelId,
                                                htPriceCalendar.getHotelId())
                                        .eq(HtPriceCalendarHmein::getRoomTypeCode, htPriceCalendar.getRoomTypeCode())
                                        .eq(HtPriceCalendarHmein::getCalDate, htPriceCalendar.getCalDate())
                                        .eq(HtPriceCalendarHmein::getSourceId, htPriceCalendar.getSourceId()));
                    });
                } catch (Exception e) {
                    log.error("syncHmeinRatesTaskPop error", e);
                }
            });
        });
        if (incrFlag.get()) {
            log.info("main-incrHotel={}", hmeinService.parseInnId(hotelId));
            // TODO send mq
            incrHotelRocketMqProducer.send(hmeinService.parseInnId(hotelId));
        }
    }

    /**
     * 得到需要入库的活动价 + 早餐数
     */
    private List<HmeinHotelRoomStatus> getHmeinHotelRoomStatus(List<RoomRates> activeRoomRatesList, String stDate) {
        return activeRoomRatesList.stream()
                .flatMap(e -> {
                    // 早餐包 ， 判断早餐情况
                    List<RoomStatusResult.RatePackages> ratePackages = e.getRatePackages();
                    // 对应日期的房态
                    RoomRateDetailDailys roomRateDetailDailys = e.getRoomRateDetailDailys().stream()
                            .filter(f -> DateUtil.compareDateWithString(f.getStDate(), stDate))
                            .findFirst().orElse(new RoomRateDetailDailys());
                    /**
                     * 经济型酒店 ，rateCode (BFM2 , BFM0) ratePackages属性中 ， 1.空列表，无早 2.BFM2，双早
                     * 高星酒店 ， rateCode (NRTMC1) ratePackages属性中 ， 1.空列表，无早 2.BFM1 单早 3.BFM2，双早
                     *
                     */
                    // 无早情况
                    if (CollectionUtils.isEmpty(ratePackages)) {
                        return Lists.newArrayList(HmeinHotelRoomStatus.builder()
                                .breakfastCount(0)
                                .price(roomRateDetailDailys.getPrs1())
                                .rateCode(e.getRateCode())
                                .build()).stream();
                    } else {
                        // 对早餐情况分别判断
                        return ratePackages.stream().map(f -> {
                            // 默认取价格1
                            BigDecimal price = roomRateDetailDailys.getPrs1();
                            // 默认取早餐数1 ， 高星酒店的情况,无早不会进入以下情况
                            Integer breakfastCount = 1;
                            // 双早情况
                            if (f.getCode().equals("BFM2")) {
                                breakfastCount = 2;
                                // 当适用人数不为1时，取价格2
                                if (f.getPeopleNum() > 1) {
                                    price = roomRateDetailDailys.getPrs2();
                                }
                            }
                            return HmeinHotelRoomStatus.builder()
                                    .breakfastCount(breakfastCount)
                                    .price(price)
                                    .rateCode(e.getRateCode())
                                    .build();
                        }).collect(Collectors.toList()).stream();
                    }
                })
                // 根据活动码，筛选最低价格 ,只保留一个
                .collect(Collectors.groupingBy(HmeinHotelRoomStatus::getRateCode,
                        Collectors.minBy(Comparator.comparing(HmeinHotelRoomStatus::getPrice))))
                .entrySet()
                .stream().filter(e -> e.getValue().isPresent())
                .map(e -> HmeinHotelRoomStatus.builder()
                        .breakfastCount(e.getValue().get().getBreakfastCount())
                        .price(e.getValue().get().getPrice())
                        .rateCode(e.getKey())
                        .build())
                .collect(Collectors.toList());
    }

    @Override
    public void syncHeminRoomRepairPush() {
        try {
            List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(
                    new LambdaQueryWrapper<HtHotelStdInfo>().eq(HtHotelStdInfo::getSourceCode,
                            SourceEnum.HMEIN.getCode()));
            if (CollectionUtils.isNotEmpty(htHotelStdInfos)
                    && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_MAIN_HMEIN_ROOM_REPAIR_LIST) <= 0) {
                redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_HMEIN_ROOM_REPAIR_LIST,
                        htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncHeminRoomRepairPop(String appId, String memberNo) {
        roomRepairTaskPopPool.execute(() -> new SyncHmeinRoomRepairTaskThread(appId, memberNo));
    }

    @Override
    public void syncHeminRoomRepair(String appId, String hotelId, String memberNo, String start, String end) {
        // 查询房态
        RoomStatusResult roomStatus = hmeinService.getRoomStatus(appId, memberNo, hotelId, null, start, end);
        // 将当前酒店下所有房型全部置为不可售
        htRoomTypeStdInfoService.update(null,
                new LambdaUpdateWrapper<HtRoomTypeStdInfo>()
                        .set(HtRoomTypeStdInfo::getRoomStatus, 0)
                        .eq(HtRoomTypeStdInfo::getHotelId, hotelId));
        if (roomStatus.getFail()) {
            return;
        }

        roomStatus.getProducts().forEach(products -> {
            RoomType roomType = products.getRoomType();
            // 将获取到房态的房型状态改为1
            htRoomTypeStdInfoService.update(null,
                    new LambdaUpdateWrapper<HtRoomTypeStdInfo>()
                            .set(HtRoomTypeStdInfo::getRoomStatus, 1)
                            .eq(HtRoomTypeStdInfo::getHotelId, hotelId)
                            .eq(HtRoomTypeStdInfo::getRoomTypeCode, roomType.getRoomTypeCode()));
        });
    }

    @Override
    public void syncHmeinPicPush() {
        if (redisUtil.setENx(RedisKeyConstant.HOTEL_MAIN_HMEIN_PIC_PUSH, PUSH_EXPIRY,
                RedisKeyConstant.HOTEL_MAIN_HMEIN_PIC_PUSH)) {
            try {
                List<String> hotels = htHotelStdInfoDao.selectList(
                        new LambdaQueryWrapper<HtHotelStdInfo>().eq(HtHotelStdInfo::getSourceCode,
                                SourceEnum.HMEIN.getCode())).stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList());
                if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(hotels)
                        && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_MAIN_HMEIN_PIC_LIST) <= 0) {
                    redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_HMEIN_PIC_LIST,
                            new ArrayList<>(hotels));
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    @Override
    public void syncHmeinPicPop(String appId) {
        hotelIdListTaskFastPopPool.execute(() -> new SyncHmeinPicTaskThread(appId));
    }

    @Override
    public void syncHmeinPic(String appId, String hotelId) throws IOException {
        List<String> hotelPic = hmeinService.getHotelPic(appId, hmeinService.parseHotelId(hotelId));

        for (int i = 0; i < hotelPic.size(); i++) {
            String sourcePic = hotelPic.get(i);
            HtHotelStdImage htHotelStdImage = htHotelStdImageService.getOne(
                    new LambdaQueryWrapper<HtHotelStdImage>()
                            .eq(HtHotelStdImage::getHotelId, hotelId)
                            .eq(HtHotelStdImage::getImageType, 1)
                            .eq(HtHotelStdImage::getSourceUrl, sourcePic));
            if (Objects.nonNull(htHotelStdImage)) {
                continue;
            }
            if (i == 0) {
                uploadPic(sourcePic, hotelId, null, 1, i);
            }
            uploadPic(sourcePic, hotelId, null, 2, i);
        }
        List<HotelRmMc> hotelRmMcs = hmeinService.getRoomType(appId, hmeinService.parseHotelId(hotelId));
        hotelRmMcs.forEach(roomType -> {
            if (Objects.isNull(roomType.getPicUrl())) {
                return;
            }
            String[] urls = roomType.getPicUrl().split(",");
            for (int i = 0; i < urls.length; i++) {
                String sourcePic = urls[i];
                if (!sourcePic.contains("336-225")) {
                    return;
                }
                HtHotelStdImage htHotelStdImage = htHotelStdImageService.getOne(
                        new LambdaQueryWrapper<HtHotelStdImage>()
                                .eq(HtHotelStdImage::getHotelId, hotelId)
                                .eq(HtHotelStdImage::getImageType, 3)
                                .eq(HtHotelStdImage::getRoomTypeCode, roomType.getStRmTypeCd())
                                .eq(HtHotelStdImage::getSourceUrl, sourcePic));
                if (Objects.nonNull(htHotelStdImage)) {
                    continue;
                }
                try {
                    uploadPic(sourcePic, hotelId, roomType.getStRmTypeCd(), 3, i);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
        });
    }

    private Boolean validatePrice(BigDecimal retailPrice, BigDecimal purchasePrice, Integer quota, HtPriceCalendarHmein htPriceCalendar) {
        if (Objects.isNull(htPriceCalendar)) {
            return true;
        }
        boolean flag = false;
        if (Objects.nonNull(htPriceCalendar.getQuota()) && Objects.nonNull(quota)) {
            flag = !htPriceCalendar.getQuota().equals(quota) && quota <= 10;
        }

        if (Objects.nonNull(htPriceCalendar.getRetailPrice()) && Objects.nonNull(retailPrice)) {
            flag = htPriceCalendar.getRetailPrice().compareTo(retailPrice) != 0 || flag;
        }

        if (Objects.nonNull(htPriceCalendar.getPurchasePrice()) && Objects.nonNull(purchasePrice)) {
            flag = htPriceCalendar.getPurchasePrice().compareTo(purchasePrice) != 0 || flag;
        }

        if (Objects.nonNull(htPriceCalendar.getQuota()) && Objects.nonNull(quota)) {
            flag = !htPriceCalendar.getQuota().equals(quota) && quota <= 10 || flag;
        }

        return flag;
    }

    private HtPriceCalendarHmein getRoomCalDatePriceCalendar(String roomType, Date date, List<HtPriceCalendarHmein> htPriceCalendars) {
        return htPriceCalendars.stream()
                .filter(e -> e.getRoomTypeCode().equals(roomType)
                        && DateUtils.isSameDay(e.getCalDate(), date))
                .findFirst().orElse(null);
    }

    private void updateQuota(String hotelId, String roomTypeCode) {
        htPriceCalendarHmeinService.update(null, new LambdaUpdateWrapper<HtPriceCalendarHmein>()
                .set(HtPriceCalendarHmein::getQuota, 0)
                .eq(HtPriceCalendarHmein::getHotelId, hotelId)
                .eq(HtPriceCalendarHmein::getRoomTypeCode, roomTypeCode));
    }


    private void uploadPic(String sourcePic, String hotelId, String roomType, Integer type, Integer index) {
//        uploadPic(sourcePic, hotelId, roomType, type, index, "png");
//        uploadPic(sourcePic, hotelId, roomType, type, index, "webp");
        uploadPic(sourcePic, hotelId, roomType, type, index, "jpg");
    }

    private void uploadPic(String sourcePic, String hotelId, String roomType, Integer type, Integer index, String suffix) {
        String fileName;
        if (StringUtils.isNotBlank(roomType)) {
            fileName = String.format("HMEIN/%s_%s_%s_%s.%s", hotelId, roomType, type, index, suffix);
        } else {
            fileName = String.format("HMEIN/%s_%s_%s.%s", hotelId, type, index, suffix);
        }
        String target = OssUtil.updaload(sourcePic, fileName);
        savePicInfo(hotelId, roomType, type, sourcePic, target);
    }


    private void savePicInfo(String hotelId, String roomTypeCode, Integer type, String sourceUrl, String imageUrl) {
        HtHotelStdImage image = new HtHotelStdImage();
        image.setHotelId(hotelId);
        image.setRoomTypeCode(roomTypeCode);
        image.setImageType(type);
        image.setSourceUrl(sourceUrl);
        image.setImageUrl(imageUrl);
        htHotelStdImageService.save(image);
    }
}
